using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml.Serialization;
using Schedulr.Helpers;
using Schedulr.Properties;

namespace Schedulr.Configuration
{
    /// <summary>
    /// Represents a picture.
    /// </summary>
    [Serializable]
    public class Picture : INotifyPropertyChanged
    {
        #region Constants
        
        public const string PropertyNameFileName = "FileName";
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "NameTags")]
        public const string PropertyNameTags = "Tags";
        public const string PropertyNamePhotoset = "Photoset";
        public const string PropertyNameIsPublic = "IsPublic";
        public const string PropertyNameIsFamily = "IsFamily";
        public const string PropertyNameIsFriend = "IsFriend";
        public const string PropertyNameTitle = "Title";
        public const string PropertyNameDescription = "Description";
        public const string PropertyNameSetIds = "SetIds";
        public const string PropertyNameGroupIds = "GroupIds";
        public const string PropertyNameUploadWithNext = "UploadWithNext";

        #endregion

        #region Properties

        private string fileName;

        /// <summary>
        /// Gets or sets the name of the file.
        /// </summary>
        [XmlAttribute]
        public string FileName
        {
            get { return this.fileName; }
            set { this.fileName = value; OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameFileName)); }
        }

        private string tags;

        /// <summary>
        /// Gets or sets the tags.
        /// </summary>
        [XmlAttribute]
        public string Tags
        {
            get { return this.tags; }
            set { this.tags = value; OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameTags)); }
        }

        private string photoset;

        /// <summary>
        /// [Obsolete: Use SetIds property instead.] Gets or sets the photoset.
        /// </summary>
        [XmlAttribute]
        public string Photoset
        {
            get { return this.photoset; }
            set { this.photoset = value; OnPropertyChanged(new PropertyChangedEventArgs(PropertyNamePhotoset)); }
        }

        private bool isPublic;

        /// <summary>
        /// Gets or sets a value indicating whether this picture is public.
        /// </summary>
        [XmlAttribute]
        public bool IsPublic
        {
            get { return this.isPublic; }
            set { this.isPublic = value; OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameIsPublic)); }
        }

        private bool isFamily;

        /// <summary>
        /// Gets or sets a value indicating whether this picture is visible to family.
        /// </summary>
        [XmlAttribute]
        public bool IsFamily
        {
            get { return this.isFamily; }
            set { this.isFamily = value; OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameIsFamily)); }
        }

        private bool isFriend;

        /// <summary>
        /// Gets or sets a value indicating whether this picture is visible to friends.
        /// </summary>
        [XmlAttribute]
        public bool IsFriend
        {
            get { return this.isFriend; }
            set { this.isFriend = value; OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameIsFriend)); }
        }

        private string title;

        /// <summary>
        /// Gets or sets the title.
        /// </summary>
        [XmlElement]
        public string Title
        {
            get { return this.title; }
            set { this.title = value; OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameTitle)); }
        }

        private string description;

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        [XmlElement]
        public string Description
        {
            get { return this.description; }
            set
            {
                this.description = value;

                // Fix up multiline strings (these are normalized by the XmlSerializer).
                if (this.description != null && !this.description.Contains("\r\n"))
                {
                    this.description = this.description.Replace("\n", "\r\n");
                }

                OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameDescription));
            }
        }

        private string[] setIds = new string[0];

        /// <summary>
        /// Gets or sets the id's of the sets to associate with this picture.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays"), XmlAttribute]
        public string[] SetIds
        {
            get { return this.setIds; }
            set
            {
                // The XML serializer has some strange behavior regarding arrays where elements can be empty strings.
                // Loop over the deserialized entries and remove empty strings to fix that.
                var trimmedIds = new List<string>();
                if (value != null)
                {
                    foreach (var id in value)
                    {
                        if (!string.IsNullOrEmpty(id))
                        {
                            trimmedIds.Add(id);
                        }
                    }
                }
                this.setIds = trimmedIds.ToArray();
                OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameSetIds));
            }
        }

        private string[] groupIds = new string[0];

        /// <summary>
        /// Gets or sets the id's of the groups to associate with this picture.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays"), XmlAttribute]
        public string[] GroupIds
        {
            get { return this.groupIds; }
            set
            {
                // The XML serializer has some strange behavior regarding arrays where elements can be empty strings.
                // Loop over the deserialized entries and remove empty strings to fix that.
                var trimmedIds = new List<string>();
                if (value != null)
                {
                    foreach (var id in value)
                    {
                        if (!string.IsNullOrEmpty(id))
                        {
                            trimmedIds.Add(id);
                        }
                    }
                }
                this.groupIds = trimmedIds.ToArray();

                OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameGroupIds));
            }
        }

        private bool uploadWithNext;

        /// <summary>
        /// Gets or sets a value indicating whether this picture should be uploaded together with the next picture in the queue.
        /// </summary>
        [XmlAttribute]
        public bool UploadWithNext
        {
            get { return this.uploadWithNext; }
            set { this.uploadWithNext = value; OnPropertyChanged(new PropertyChangedEventArgs(PropertyNameUploadWithNext)); }
        }

        #endregion

        #region Derived Properties

        /// <summary>
        /// Gets the short name of the file.
        /// </summary>
        [XmlIgnore]
        public string ShortFileName
        {
            get
            {
                if (string.IsNullOrEmpty(this.fileName))
                {
                    return this.fileName;
                }
                else
                {
                    return Path.GetFileName(this.fileName);
                }
            }
        }

        /// <summary>
        /// Gets the size of the file as a string.
        /// </summary>
        [XmlIgnore]
        public long FileSize
        {
            get
            {
                if (string.IsNullOrEmpty(this.fileName) || !File.Exists(this.fileName))
                {
                    return -1;
                }
                else
                {
                    return new FileInfo(this.fileName).Length;
                }
            }
        }

        /// <summary>
        /// Gets the size of the file as a string.
        /// </summary>
        [XmlIgnore]
        public string FileSizeString
        {
            get
            {
                long fileSize = this.FileSize;
                if (fileSize < 0)
                {
                    return string.Empty;
                }
                else
                {
                    return Converter.GetBytesAsString(fileSize);
                }
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Picture"/> class.
        /// </summary>
        public Picture()
        {
            this.IsFamily = Settings.Default.DefaultFamily;
            this.IsFriend = Settings.Default.DefaultFriend;
            this.IsPublic = Settings.Default.DefaultPublic;
            this.Description = Settings.Default.DefaultDescription;
            this.Tags = Settings.Default.DefaultTags;
            this.Title = Settings.Default.DefaultTitle;
            this.setIds = Converter.CollectionToArray(Settings.Default.DefaultSets);
            this.groupIds = Converter.CollectionToArray(Settings.Default.DefaultGroups);
            this.UploadWithNext = Settings.Default.DefaultUploadWithNext;
        }
        
        /// <summary>
        /// Initializes a new instance of the <see cref="Picture"/> class.
        /// </summary>
        /// <param name="picture">The picture to clone.</param>
        public Picture(Picture picture)
        {
            this.Description = picture.Description;
            this.FileName = picture.FileName;
            this.IsFamily = picture.IsFamily;
            this.IsFriend = picture.IsFriend;
            this.IsPublic = picture.IsPublic;
            this.Tags = picture.Tags;
            this.Title = picture.Title;
            this.SetIds = picture.SetIds;
            this.GroupIds = picture.GroupIds;
            this.UploadWithNext = picture.UploadWithNext;
        }

        #endregion

        #region INotifyPropertyChanged Implementation

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the <see cref="E:PropertyChanged"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, e);
            }
        }

        #endregion
    }
}